#include "DM.h"
#include "Timer.h"
#include "TimerImp.h"
#include "EffCap.h"
#include "message.h"
#include <string>
#include <cassert>

/*
//----------------------------------------------------------------------------------------------------------//
class Arc_C
{
    public:
        vector<float> _vInputTrans;
        vector<double> _vDelayFromPin;
        vector<string> _vNameFromPin;
        string _nameToPin;
        LibCell_C* _pLibCell;
};
//----------------------------------------------------------------------------------------------------------//
class Timer_C::Imp_C
{
    public: //data
        DMMgr_C* _pDMMgr;
        Arc_C* _pArc;
        vector<vector<pair<BoundaryNode_C*,float> > > _vvTrans;
        vector<vector<pair<BoundaryNode_C*,float> > > _vvDelay;

    public: //function
        Imp_C(DMMgr_C* p_pDMMgr);
        void calcNet(Net_C* p_pNet,Timer_C::TransType_E p_eInputTrans);
        void prepareArcInfo(BoundaryNode_C* p_pFromNode,TransType_E p_eInputTrans);
        void estimateTiming_allArcs(Net_C* p_pNet,TransType_E p_eInputTrans);
        void worstPropagation(TransType_E p_eInputTrans);
        void calcSlack();
};
*/
//----------------------------------------------------------------------------------------------------------//
Timer_C::Imp_C::Imp_C(DMMgr_C* p_pDMMgr) : _pDMMgr(p_pDMMgr) 
{
    _pArc = new Arc_C;
}
//----------------------------------------------------------------------------------------------------------//
Timer_C::Timer_C(DMMgr_C* p_pDMMgr)
{
    _pImp = new Imp_C(p_pDMMgr);
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::Imp_C::prepareArcInfo(BoundaryNode_C* p_pFromNode,TransType_E p_eInputTrans)
{
    assert(_pArc);
    _pArc->_vInputTrans.resize(0);
    _pArc->_vDelayFromPin.resize(0);
    _pArc->_vNameFromPin.resize(0);
    if(Node_C::ePI==p_pFromNode->getType()){
        PINode_C* pFromPI = static_cast<PINode_C*> (p_pFromNode); 
        _pArc->_pLibCell = pFromPI->getLibCell();
        _pArc->_nameToPin = "o";
        _pArc->_vNameFromPin.push_back("a");
        _pArc->_vDelayFromPin.push_back(0.0); //TODO read "delay w.r.t clk" in SDC file
        if(Timer_C::eRise==p_eInputTrans) {_pArc->_vInputTrans.push_back(pFromPI->getDriverTransRise());} 
        else                              {_pArc->_vInputTrans.push_back(pFromPI->getDriverTransFall());}
    } else {
        PinNode_C* pArcToPin= static_cast<PinNode_C*> (p_pFromNode);
        _pArc->_pLibCell = pArcToPin->getInst()->getLibCell();
        if(!_pArc->_pLibCell->is_seq()){
            _pArc->_nameToPin = pArcToPin->getPinName();
            const vector<PinNode_C*>& vArcFromPin = pArcToPin->getInst()->getFromNode();
            for(unsigned int iArc=0;iArc<vArcFromPin.size();++iArc){
                PinNode_C* pArcFromPin = vArcFromPin[iArc];
                _pArc->_vNameFromPin.push_back(pArcFromPin->getPinName());
                if(Timer_C::eRise==p_eInputTrans) {
                    _pArc->_vInputTrans.push_back(vArcFromPin[iArc]->getTransRise()); 
                    _pArc->_vDelayFromPin.push_back(vArcFromPin[iArc]->getDelayRise());
                }
                else {
                    _pArc->_vInputTrans.push_back(pArcFromPin->getTransFall());
                    _pArc->_vDelayFromPin.push_back(vArcFromPin[iArc]->getDelayFall());
                }
            }
        } else {
            _pArc->_nameToPin = pArcToPin->getPinName();
            _pArc->_vNameFromPin.push_back("ck");
            _pArc->_vInputTrans.push_back(0.0);
            _pArc->_vDelayFromPin.push_back(0.0);
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::Imp_C::estimateTiming_allArcs(Net_C* p_pNet,TransType_E p_eInputTrans)
{
    _vvTrans.resize(0);
    _vvDelay.resize(0);
    for(unsigned int iArc=0;iArc<_pArc->_vNameFromPin.size();++iArc){
        EffCap_C* pEffCap; 
        if(Timer_C::eRise==p_eInputTrans){
            const LibParserLUT& rTransLUTFall = _pArc->_pLibCell->getTimingLUT(_pArc->_vNameFromPin[iArc],_pArc->_nameToPin,nsLibCell::e_fall_tran);
            const LibParserLUT& rDelayLUTFall = _pArc->_pLibCell->getTimingLUT(_pArc->_vNameFromPin[iArc],_pArc->_nameToPin,nsLibCell::e_fall_delay);
            pEffCap = new EffCap_C(p_pNet,_pArc->_vInputTrans[iArc],_pArc->_vDelayFromPin[iArc],rTransLUTFall,rDelayLUTFall);
        } else {
            const LibParserLUT& rTransLUTRise = _pArc->_pLibCell->getTimingLUT(_pArc->_vNameFromPin[iArc],_pArc->_nameToPin,nsLibCell::e_rise_tran);
            const LibParserLUT& rDelayLUTRise = _pArc->_pLibCell->getTimingLUT(_pArc->_vNameFromPin[iArc],_pArc->_nameToPin,nsLibCell::e_rise_delay);
            pEffCap = new EffCap_C(p_pNet,_pArc->_vInputTrans[iArc],_pArc->_vDelayFromPin[iArc],rTransLUTRise,rDelayLUTRise);
        }
        pEffCap->run();
        const vector<pair<BoundaryNode_C*,float> >& vTrans = pEffCap->getTrans();
        const vector<pair<BoundaryNode_C*,float> >& vDelay = pEffCap->getDelay();
        _vvTrans.push_back(vTrans);
        _vvDelay.push_back(vDelay);
        delete pEffCap;
    }
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::Imp_C::worstPropagation(TransType_E p_eInputTrans)
{
    for(unsigned int iBNode=0;iBNode<_vvTrans[0].size();++iBNode){
        BoundaryNode_C* pBNode = _vvTrans[0][iBNode].first;
        float fTransWst(0);
        float fDelayWst(0);
        for(unsigned int iArc=0;iArc<_vvTrans.size();++iArc){
            float fTrans = _vvTrans[iArc][iBNode].second;
            float fDelay = _vvDelay[iArc][iBNode].second;
            if(fTrans>fTransWst) {fTransWst=fTrans;}
            if(fDelay>fDelayWst) {fDelayWst=fDelay;}
        }
        if(Timer_C::eRise==p_eInputTrans){
            pBNode->resetTimingFall();
            if(pBNode->getTransFall()<fTransWst) {pBNode->setTransFall(fTransWst);}
            if(pBNode->getDelayFall()<fDelayWst) {pBNode->setDelayFall(fDelayWst);}
        } else {
            pBNode->resetTimingRise();
            if(pBNode->getTransRise()<fTransWst) {pBNode->setTransRise(fTransWst);}
            if(pBNode->getDelayRise()<fDelayWst) {pBNode->setDelayRise(fDelayWst);}
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
//Note: Now we only deal timing arc of "negative_unate" 
void Timer_C::Imp_C::calcNet(Net_C* p_pNet,Timer_C::TransType_E p_eInputTrans)
{
    prepareArcInfo(p_pNet->getFromNode(),p_eInputTrans);
    estimateTiming_allArcs(p_pNet,p_eInputTrans);
    worstPropagation(p_eInputTrans);
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::Imp_C::calcSlack()
{
    //Seq & PO
    const vector<Inst_C*>& vSeq = _pDMMgr->getSeqInst();
    for(unsigned int iSeq=0;iSeq<vSeq.size();++iSeq){
        for(unsigned int iFrom=0;iFrom<vSeq[iSeq]->getFromNode().size();++iFrom){
            PinNode_C* pSeqFromPin = vSeq[iSeq]->getFromNode()[iFrom];
            if(0==strcmp("ck",pSeqFromPin->getName().c_str())) {continue;}
            pSeqFromPin->setSlackRise(_pDMMgr->getClockPeriod()-pSeqFromPin->getDelayRise());
            pSeqFromPin->setSlackFall(_pDMMgr->getClockPeriod()-pSeqFromPin->getDelayFall());
        }
    }
    const map<string,PONode_C*>& mapPONode = _pDMMgr->getPONode();
    for(map<string,PONode_C*>::const_iterator it=mapPONode.begin();it!=mapPONode.end();++it){
        PONode_C* pPO = it->second;
        pPO->setSlackRise(_pDMMgr->getClockPeriod()-pPO->getDelayRise());
        pPO->setSlackFall(_pDMMgr->getClockPeriod()-pPO->getDelayFall());
    }
    //TODO backward
    /*
    const vector<Inst_C*>& vInst = _pDMMgr->getInstByVector();
    for(vector<Inst_C*>::const_reverse_iterator it=vInst.rbegin();it!=vInst.rend();++it){
        Inst_C* pInst = *it;
        if(pInst->isSeq()) {continue;}
        const vector<Inst_C*>& vReceiver = pInst->getReceiver();
        for(unsigned int iR=0;iR<vReceiver.size();++iR){
            Inst_C* pReceiver = vReceiver[iR];
        }
    }
    */
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::runByEffCap()
{
    InfoMsg("[Timer] Timing analysis by [R.Puri GLSVLSI 2002]\n");
    for(unsigned int iNet=0;iNet<_pImp->_pDMMgr->getNetByVector().size();++iNet){
        Net_C* pNet = _pImp->_pDMMgr->getNetByVector()[iNet];
        _pImp->calcNet(pNet,eRise);
        _pImp->calcNet(pNet,eFall);
    }
    //_pImp->calcSlack();
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::runByPTime()
{
#ifdef PRIMETIME
    InfoMsg("[Timer] Timing analysis by PrimeTime\n");
#else
    InfoMsg("[Timer] PrimeTime is unavailable\n");
#endif 
    
    while(Timer_C::ePT_Fail==_pImp->runPTime()) {}
    _pImp->parseTiming();
    //parseCeff();
}
//----------------------------------------------------------------------------------------------------------//
#ifndef PRIMETIME
Timer_C::PTime_Status Timer_C::Imp_C::runPTime()
{
    return Timer_C::ePT_Success;
}
//----------------------------------------------------------------------------------------------------------//
void Timer_C::Imp_C::parseTiming()
{
}
#endif 


